/**
 * @file Domain.h
 * @author Lishijie (lsj1018845759@outlook.com)
 * @brief 
 * @version 0.1
 * @date 2020-11-08
 * 
 * @copyright Copyright (c) 2020
 * 
 */
#ifndef _DOMAIN_LHW_
#define _DOMAIN_LHW_
#include "Point.h"

/**
 * @brief  计算区域类domain，所有计算区域类的基类。
 * 
 */
#define TEMPLATE template<Dimension DIM>

TEMPLATE
class Domain
{
typedef std::vector<Point<DIM> > PointList;
typedef int PointNum;
typedef int Rank;

public:

    /**
     * @brief Construct a new Domain object
     */
    Domain();

    /**
     * @brief Construct a new Domain object by vector<Point<DIM> > in std namespace;
     *        利用({{0,0},{1,0},{1,1},{0,1}})可以进行初始化；
     */
    Domain(const PointNum);

    /**
     * @brief Destroy the Domain object
     */
    ~Domain();

    /**
     * @brief 重载下标运算符operator[]
     * @param r 指定第几个几何点；
     * @return Point<DIM>& 几何点引用；
     */
    Point<DIM>& operator[](Rank r){return _Pointlist[r];};

protected:
    PointList _Pointlist;
};

TEMPLATE
Domain<DIM>::Domain()
{    
}

TEMPLATE
Domain<DIM>::Domain(const PointNum num)
{
    _Pointlist.resize(num);
}

TEMPLATE
Domain<DIM>::~Domain()
{
}

TEMPLATE
struct Boundary
{
    std::vector<Point<DIM> > _segementpointlist;
    bool is_on_boundary(Point<DIM> point);
    void setsegment(std::vector<Point<DIM>> temp){_segementpointlist = temp;};
    Boundary(){};
    Boundary(std::vector<Point<DIM>> temp){_segementpointlist = temp;};
};

template<>
bool Boundary<2>::is_on_boundary(Point<2> point)
{
    double x1 = _segementpointlist[0][0];
    double y1 = _segementpointlist[0][1];
    double x2 = _segementpointlist[1][0];
    double y2 = _segementpointlist[1][1];
    double x = point[0];
    double y = point[1];
    if(x1 > x2)//按照x从小到大
    {
        std::swap(x1,x2);
        std::swap(y1,y2);
    }
    bool ans = false;
    if(y1 == y2)
    {
        if(x >= x1 && x <= x2)
            ans = (y == y1);    
    }
    else if(x1 == x2)
    {
        if(y2 > y1)
        {
            ans = ((x == x1) &&( y >= y1 && y <= y2));
        }
        else
        {
            ans = ((x == x1) &&( y >= y2 && y <= y1 ));
        }      
    } 
    return ans;
}

struct IsRectangleException : public std::logic_error
{

    IsRectangleException(std::vector<Point<2U>> p):std::logic_error("Yours input doesn't make a reatangle in 2D!")
    {
        this -> p = p;
    }
    private:
    std::vector<Point<2U>> p;
};

/**
 * @brief 矩形计算区域类，继承Domain<2U>;
 * 
 */
class RectangleDomain:public Domain<2U>
{   
public:
    typedef Domain<2U> _base;
    using _base::_Pointlist;
public:
    /**
    * @brief Construct a new Rectangle Domain object
    * 
    */
    RectangleDomain(){_Pointlist.resize(4);};
    /**
     * @brief Construct a new Rectangle Domain object
     * 
     */
    RectangleDomain(std::vector<Point<2>>);
    /**
     * @brief Destroy the Rectangle Domain object
     */
    ~RectangleDomain(){};
    /**
     * @brief 设置或返回矩形区域左下角
     * 
     * @return Point<2U>& 几何点引用
     */
    Point<2U>& LeftBottomPoint();
    /**
     * @brief 设置或返回矩形区域右下角
     * 
     * @return Point<2U>& 几何点引用
     */
    Point<2U>& RightBottomPoint();
    /**
     * @brief 设置或返回矩形区域右上角
     * 
     * @return Point<2U>& 几何点引用
     */
    Point<2U>& RightTopPoint();
    /**
     * @brief 设置或返回矩形区域左上角
     * 
     * @return Point<2U>& 几何点引用
     */
    Point<2U>& LeftTopPoint();
    /**
     * @brief 返回矩形区域的所有边界；
     * 
     * @return std::vector<Boundary<2>> 
     */
    std::vector<Boundary<2>> boundary(); 
    /**
     * @brief 返回指定边界，从下标为零开始，对于矩形区域逆时针编号；
     * 
     * @param i 
     * @return Boundary<2> 
     */
    Boundary<2> boundary(int i);
};

RectangleDomain::RectangleDomain(std::vector<Point<2>> p) 
{
     _Pointlist = p;
}
Point<2U>& RectangleDomain::LeftBottomPoint()
{
    return _Pointlist[0];
}

Point<2U>& RectangleDomain::RightBottomPoint()
{
    return _Pointlist[1];
}
Point<2U>& RectangleDomain::RightTopPoint()
{
    return _Pointlist[2];
}

Point<2U>& RectangleDomain::LeftTopPoint()
{

    return _Pointlist[3];
}

std::vector<Boundary<2>> RectangleDomain::boundary()
{
    std::vector<Boundary<2>> temp;
    
    for(int i = 0;i < 3;i++)
    {
        std::vector<Point<2>> p;
        p.push_back(_Pointlist[i]);
        p.push_back(_Pointlist[i+1]);
        Boundary<2> b(p);
        temp.push_back(b);
    }
    std::vector<Point<2>> p4(2);
    p4[0] = _Pointlist[3];
    p4[1] = _Pointlist[0];
    temp.push_back(p4);
    return temp;
}
Boundary<2> RectangleDomain::boundary(int i)
{
    if(i > 3)
        std::exit(-1);
    std::vector<Point<2>> p;
    if(i != 3)
    {
        p.push_back(_Pointlist[i]);
        p.push_back(_Pointlist[i+1]);
        Boundary<2> b(p);
        return b;
    }
    else
    {
        p.push_back(_Pointlist[3]);
        p.push_back(_Pointlist[0]);
        Boundary<2> b(p); 
        return b;
    }
   
}

#undef TEMPLATE

#else
#endif
